<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    ul, li {
      list-style: none;
      margin: 0;
      padding: 0;
    }

    ul {
      padding-left: 24px;
      border-left: 1px dashed #dddddd;
    }

    .box {
      font-size: 14px;
      line-height: 1.5;
      position: relative;
    }

    .box > ul {
      display: none;
    }

    .box.box__open > ul {
      display: block;
    }

    .ellipsis__hide {
      display: none;
    }

    .box-pointer {
      position: absolute;
      cursor: pointer;
      display: inline-block;
      text-align: center;
      width: 14px;
      left: -14px;
    }

    .box-bracket {
      display: inline-block;
    }
  </style>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #div1 {
      width: 100vw;
      float: left;
      overflow: auto;
      padding-left: 16px;
      background-color: #e8e8e8;
      box-sizing: border-box;
    }

    #div2 {
      width: 0;
      float: right;
      overflow: auto;
      padding-left: 16px;
      background-color: #9dde9d;
      box-sizing: border-box;
    }
  </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
  // 解析函数
  function getQuery (url) {
    // 创建一个 url 对象
    const urlObj = new URL(url)
    // console.log(urlObj)

    // 1. search 字段：?后面的字符串，包含?
    // 2. searchParams 字段：URLSearchParams 对象，search 中的各个查询参数。等价于 new URLSearchParams(search)
    const { search, searchParams } = urlObj

    const queryParams = {}
    for (const [k, v] of searchParams) {
      // 处理 v 属性 v参数字段是由$符拼接的字符串
      if (k === 'v') {
        const val = decodeURIComponent(v)
        const vals = val.split('$')
        queryParams[k] = vals.reduce((rst, item) => {
          const [key, value] = item.split('=')
          // 处理 p0 属性，它是一个 json 字符串
          if (key === 'p0') {
            rst[key] = JSON.parse(value)
          } else {
            rst[key] = value
          }
          return rst
        }, {})
      } else {
        queryParams[k] = decodeURIComponent(v)
      }
    }
    return queryParams
  }

  /**
   * 创建dom元素
   *
   * @param type 创建的dom标签类型
   * @param props 所创建dom上的属性
   * @param children 所创建dom的子元素
   * */
  function _createElement (type, props, ...children) {
    const dom = document.createElement(type || 'div')
    if (props) {
      for (const key in props) {
        dom.setAttribute(key, props[key])
      }
    }
    if (children) {
      for (const child of children) {
        if (child === null || child === void 0) {
          dom.appendChild(document.createTextNode(''))
        } else if (
          typeof child === 'string' ||
          typeof child === 'number' ||
          typeof child === 'boolean'
        ) {
          dom.appendChild(document.createTextNode(child.toString()))
        } else {
          dom.appendChild(child)
        }
      }
    }

    return dom
  }

  /**
   * 可视化函数
   *
   * @param data 可视化的数据
   * @param fieldName 父节点字段名
   * @param isLast 是否是最后一个属性
   * */
  function jsonParse (data, fieldName, isLast) {
    // 不是数组或对象 就返回一个li直接展示
    if (typeof data !== 'object') {
      return _createElement('li', null,
        fieldName ? _createElement('b', null, fieldName) : '',
        fieldName ? ': ' : '',
        data,
        isLast ? '' : ','
      )
    }

    // 是否是数组
    const _is_array = Array.isArray(data)

    // 创建容器
    const box = _createElement('div', { class: 'box box__open' })

    // 创建并追加 +或- 元素
    const boxPointer = _createElement('span', { class: 'box-pointer' }, '+')
    // 增加点击事件
    boxPointer.onclick = () => {
      if (boxPointer.textContent === '+') {
        box.className = 'box'
        ellipsis.className = ''
        boxPointer.textContent = '-'
      } else {
        box.className = 'box box__open'
        ellipsis.className = 'ellipsis__hide'
        boxPointer.textContent = '+'
      }
    }
    box.appendChild(boxPointer)

    // 创建并追加父字段名
    if (fieldName) {
      const fieldDom = _createElement('span', null,
        _createElement('b', null, fieldName),
        ': '
      )
      box.appendChild(fieldDom)
    }

    // 创建并追加左括号
    const boxBracketLeft = _createElement('span', null,
      _is_array ? '[' : '{'
    )
    box.appendChild(boxBracketLeft)

    // 创建并追加... 展开隐藏，收起显示
    const ellipsis = _createElement('span', { class: 'ellipsis__hide' }, '...')
    box.appendChild(ellipsis)

    // 创建ul容器
    const ul = document.createElement('ul')
    box.appendChild(ul)

    if (_is_array) {
      // 是数组
      for (let i = 0, len = data.length; i < len; i ++) {
        ul.appendChild(jsonParse(data[i], '', i === len - 1))
      }
    } else {
      // 是对象
      const len = Object.keys(data).length
      let count = 0
      for (const key in data) {
        count ++
        ul.appendChild(jsonParse(data[key], key, count === len))
      }
    }

    // 创建并追加右括号
    const boxBracketRight = _createElement('span', null,
      _is_array ? ']' : '}',
      isLast ? '' : ','
    )
    box.appendChild(boxBracketRight)

    return box
  }


  // const urlStr = 'https://beta-api.m.jd.com/api?appid=search-pc-java&functionId=relsearch&client=pc&clientVersion=1.0.0&uuid=17482540402232046703874&keyword=%E6%B3%A1%E8%8C%B6%E5%85%BB%E7%94%9F%E7%BA%A2%E6%9E%A3&num=10&rettype=json&type_name=relsearch&body={}&t=1752478770073'
  const urlStr = 'https://mercury.jd.com/log.gif?t=other.000000&m=UA-J2011-1&pin=jd_uJFoiemOGjRh&uid=17482540402232046703874&sid=17482540402232046703874|138&v=je%3D0%24sc%3D24-bit%24sr%3D1280x800%24ul%3Dzh-cn%24cs%3DUTF-8%24dt%3D%E5%95%86%E5%93%81%E6%90%9C%E7%B4%A2-%E4%BA%AC%E4%B8%9C%24hn%3Ddev.jd.com%24fl%3D-%24os%3Dwin%24br%3Dchrome%24bv%3D137.0.0.0%24wb%3D1748254040%24xb%3D1752460296%24yb%3D1752471232%24zb%3D138%24cb%3D216%24usc%3Ddirect%24ucp%3D-%24umd%3Dnone%24uct%3D-%24ct%3D1752483175485%24lt%3D0%24tad%3D-%24t1%3DSearch_ProductList%24t2%3DProductCard%24p0%3D%257B%2522is_active_filt%2522%253A%25220%2522%252C%2522firfilter%2522%253A%255B%2522%25E9%2585%258D%25E9%2580%2581%25E5%259C%25B0%25E5%259D%2580%2522%252C%2522%25E7%25BB%25BC%25E5%2590%2588%2522%255D%252C%2522keyword%2522%253A%2522%25E6%2589%258B%25E6%259C%25BA%2522%252C%2522pvid%2522%253A%2522b68c42c2280940debf20017ed9c1dbc1%2522%252C%2522cid%2522%253A%2522-100%2522%252C%2522touchstone_expids%2522%253A%255B%2522tsabtest%257Cbase64%257CUENUZXN0XzYyNDk4fGxvb2s6UENUZXN0XzYyNDc4fGxvb2s%257Ctsabtest%2522%252C%2522tsabtest%257Cbase64%257CUENUZXN0XzU2MDc1fFJlY2VpdmVkX3ByaWNlX1Rlc3Q%257Ctsabtest%2522%252C%2522tsabtest%257Cbase64%257CUENUZXN0XzU2OTkzfFRlc3Qx%257Ctsabtest%2522%252C%2522tsabtest%257Cbase64%257CUENUZXN0XzcxMzY2fGxvb2sz%257Ctsabtest%2522%252C%2522tsabtest%257Cbase64%257CUENUZXN0XzY0MzcxfHRlc3RzaG9w%257Ctsabtest%2522%255D%252C%2522sort_type%2522%253A%2522sort_default%2522%252C%2522caller%2522%253A%252210%2522%252C%2522refer_id%2522%253A%25224%2522%252C%2522hc_cid3%2522%253A%2522655%2522%252C%2522mtest%2522%253A%2522pc_arch_new_a3_lf%252C~sak8%257Cmixtag_sak8r%252Cbunch_b_sak8_r%252Csak8_m_pdl_lc%252Csak8_m_pdl_lc_b%252Csak8_m_ps_lc%252Csak8_m_ps_lc_b%252Csak8_m_ensa_r%252Csak8_m_am_r%252Csak8_m_rmd_r%252Csak8_m_rmso_r%252Csak8_m_rmp_r%252Csak8_m_flt_r%252Csak8_m_spm_r%252Csak8_m_ccm_lc%252Csak8_m_ccm_lc_b%252Csak8_m_rmdmc_r%252Csak8_m_mto_r%252Csak8_m_gud_r%252Csak8_m_pena_r%252Csak8_m_ls_r%252Csak8_m_rkh_r%252Csak8_m_rkb_r%252Csak8_m_re_r%252Csak8_m_mtb_r%252Csak8_m_mbl_r%252Csak8_m_rbpc_r%252Csak8_m_dq_r%252Csak8_m_rls_r%252Csak8_m_bmlp_r%252Csak8_m_dll1_r%252Csak8_m_dll2_r%252Csak8_m_dll3_r%252Csak8_m_dll4_r%252Csak8_m_clkrec_r%252Csak8_s_mq_r%252Csak8_s_krp_r%252Csak8_s_bdp_r%252Csak8_s_am_r%252Csak8_s_rbam_r%252Csak8_s_rsd_r%252Csak8_s_rspr_r%252Csak8_s_rdmc_r%252Csak8_s_bw_r%252Csak8_s_rtpc_lc%252Csak8_s_rtpc_lc_b%252Csak8_s_bmc_r%252Csak8_s_ccs_r%252Csak8_s_spmp_r%252Csak8_s_rsb_r%252Csak8_s_rsdac_r%252Csak8_s_bps_lc%252Csak8_s_bps_lc_b%252Csak8_q_qr_r%252Csak8_q_qtc_r%252Csak8_q_ao_r%252Csak8_q_bz_r%252Csak8_q_qbs_lc%252Csak8_q_qbs_lc_b%252Csak8_q_qrm_r%252Csak8_d_hsp_r%252Csak8_b_rbna_r%252Csak8_o_kss_r%252Csak8_o_ons_r%252Csak8_o_oca_r%252Csak8_o_qjoppingpu_l41832%252Csak8_o_qjatt_l41910%252Csak8_o_qjcon_l47831%252Csak8_o_qjhot_l49456%252Csak8_o_qjab_l54360%252Csak8_sc_pd_r%252Csak8_sm_pb_r%252Csak8_sm_prk_r%252Csak8_sm_prc_r%252Csak8_sm_prr_r%252Csak8_cs_sht_r%252Csak8_u_nas_r%252Ca%255Ejxts%255Esak8_nn_sscmp_r%252Ca%255Ejxts%255Esak8_nn_psfs_r%252Ca%255Ejxts%255Esak8_nn_iaps-2790_l66085%252Ca%255Ejxts%255Esak8_nn_pssc-2961_l68886%252Ca%255Ejxts%255Esak8_nn_pscs-2960_l68883%252Ca%255Ejxts%255Esak8_nn_pssi-3113_l71060%252Ca%255Esak8_nn_dwi_r%252Ca%255Esak8_nn_wuliushixiao_r%252Ca%255Esak8_nn_snr_r%252Ca%255Esak8_nn_slf_r%252Ca%255Esak8_nn_mw_r%252Ca%255Esak8_nn_pls_r%252Ca%255Esak8_nn_zsp_r%252Ca%255Esak8_nn_userneg_r%252Ca%255Esak8_nn_stxt_r%252Ca%255Esak8_nn_smps_r%252Ca%255Esak8_nn_qjrp_r%252Ca%255Esak8_nn_hnt_r%252Ca%255Esak8_nn_slti_r%252Ca%255Esak8_nn_ows_r%252Ca%255Esak8_nn_ham_r%252Ca%255Esak8_nn_lwj_r%252Ca%255Esak8_nn_lpb_r%252Ca%255Esak8_nn_ddr_r%252Ca%255Esak8_nn_lcb_r%252Ca%255Esak8_nn_lt_r%252Ca%255Esak8_nn_wmtab_r%252Ca%255Esak8_nn_pcad5_r%252Ca%255Esak8_nn_hio_r%252Ca%255Esak8_nn_searcharrivestore_r%252Ca%255Esak8_nn_tongpinjuhe-3267_l74000%252Ca%255Esak8_nn_pcsu-3059_l70497%252Ca%255Esak8_nn_lfs-3151_l72166%252Ca%255Esak8_nn_qjrp2-2373_l60900%252Ca%255Esak8_nn_altab-1875_l55680%252Ca%255Esak8_nn_rltab-3346_l75579%252Ca%255Esak8_nn_csrw-2340_l60687%252Ca%255Esak8_nn_irp-2662_l64717%252Ca%255Esak8_nn_jkdg-2983_l69158%252Ca%255Esak8_nn_vio-3013_l69850%252Ca%255Esak8_nn_tonglansangyi-3030_l70144%252Ca%255Esak8_nn_hkpick-3145_l71971%252Ca%255Esak8_nn_smtui-3413_l76484%252Ca%255Esak8_nn_dizhizujian-3231_l73841%252Ca%255Esak8_nn_stui-1287_l48170%252Ca%255Esak8_nn_lsc-2575_l63909%252Ca%255Esak8_nn_mendianchuancha-3240_l73800%252Ca%255Esak8_nn_ebpd-3286_l74368%252Ca%255Esak8_nn_lss-1577_l51733%252Ca%255Esak8_nn_lsnr-1045_r%252Ca%255Esak8%255Erb_nn_stabsr_r%252Cr%255Ea%255Esak8_nn_ldnmcr-2437_r%252Csak8%255Eaigc_nn_aius_r%252Csak8%255Eaigc_nn_rj_l57963%252Csak8%255Eaigc_nn_hua_l52860%252Csak8%255Eaigc_nn_eho_r%252Csak8%255Eaigc_nn_su_lc%252Csak8%255Eaigc_nn_nrj_r%252Csak8%255Eaigc_nn_sut-1512_r%252Cs%255Ea%255Esak8_nn_swashy_r%252Cs%255Ea%255Esak8_nn_adeffect_r%252Cs%255Ea%255Esak8_nn_dnd_r%252Cs%255Ea%255Esak8_nn_mlip_r%252Cs%255Esak8_nn_stest_r%252Cs%255Esak8_nn_dgsc_l61956%252Cs%255Esak8_nn_crnt_r%252Cs%255Ez%255Er%255Ea%255Esak8_nn_mrdw_r%252Cs%255Ez%255Er%255Ea%255Esak8_nn_prdw_r%252Cs%255Ez%255Er%255Ea%255Esak8_nn_minisy-2278_r%252Cs%255Ez%255Er%255Ea%255Esak8_nn_mdd-2252_r%252Cs%255Ez%255Er%255Ea%255Esak8_nn_ldnmc-2654_r%257C%253Bsak7%257Cmixtag_sak7r%252Ca%255Esak7_nn_lrci_r%252Ca%255Esak7_nn_ssf_r%252Ca%255Esak7_nn_shf_r%252Ca%255Esak7_nn_hwf_r%252Ca%255Esak7_nn_gfu_r%252Ca%255Esak7_nn_smd_r%252Ca%255Esak7_nn_owb_r%252Ca%255Esak7_nn_ci_r%252Ca%255Esak7_nn_waimai_r%252Ca%255Esak7_nn_rule-987_l39944%252Ca%255Esak7_nn_lf-882_l36388%252Cs%255Ea%255Esak7_nn_mla_r%252Cs%255Ea%255Esak7_nn_uswa_r%252Cs%255Ea%255Esak7_nn_bishopinfluence_r%252Cs%255Ea%255Esak7_nn_sdal_r%252Cs%255Ea%255Esak7_nn_fis_r%252Cs%255Ea%255Esak7_nn_fil_r%252Cs%255Ea%255Esak7_nn_bybt_r%252Cs%255Ea%255Esak7_nn_pricefilter2_r%252Cs%255Ea%255Esak7_nn_pricefilter1_r%252Cs%255Ea%255Esak7_nn_pf3_r%252Cs%255Ea%255Esak7_nn_lbf_r%252Cs%255Ea%255Esak7_nn_iga_r%252Cs%255Ea%255Esak7_nn_mx_r%252Cs%255Ea%255Esak7_nn_sadeffect_r%252Cs%255Ea%255Esak7_nn_fil2-211_l25894%252Cs%255Ea%255Esak7_nn_fis2-203_l25876%252Cs%255Ea%255Esak7_nn_fis3-206_l25882%252Cs%255Ea%255Esak7_nn_channelexp-1854_l55395%252Cs%255Ea%255Esak7_nn_sst-885_l36414%252Cs%255Ea%255Esak7_nn_competitorexp-3227_l73659%252Cs%255Ea%255Esak7_nn_nwss-784_l34719%252Cs%255Ea%255Esak7_nn_ifts-953_l38968%252Cs%255Ea%255Esak7_nn_lht-926_l37634%252Cs%255Ez%255Er%255Esak7_nn_apf_lc%252Cs%255Ez%255Er%255Esak7_nn_sad_r%252Cs%255Ez%255Er%255Esak7_nn_jspu114_r%252Cs%255Ez%255Er%255Esak7_nn_ldmc_r%252Cs%255Ez%255Er%255Esak7_nn_job_r%252Cs%255Ez%255Er%255Esak7_nn_jspu-111_l23311%257C%2522%252C%2522logid%2522%253A%25221752481470628.5092%2522%252C%2522cid_level%2522%253A%2522-100%2522%252C%2522secfilter%2522%253A%255B%2522%25E4%25BB%2585%25E6%2598%25BE%25E7%25A4%25BA%25E6%259C%2589%25E8%25B4%25A7%2522%255D%252C%2522mtest_act%2522%253A%2522-100%2522%252C%2522page%2522%253A1%252C%2522pos%2522%253A1%252C%2522skuid%2522%253A%252210085976605508%2522%252C%2522title%2522%253A%2522%25E5%25B0%258F%25E7%25B1%25B3%2520Redmi%2520Note13%2520%25E7%25BA%25A2%25E7%25B1%25B3note13%25205G%25E6%2589%258B%25E6%259C%25BA%2520%25E7%25BA%25A2%25E7%25B1%25B3%25E6%2589%258B%25E6%259C%25BA%2520%25E5%25B0%258F%25E7%25B1%25B3%25E6%2589%258B%25E6%259C%25BA%2520%25E6%2598%259F%25E6%25B2%2599%25E7%2599%25BD%25205G%25E5%2585%25A8%25E7%25BD%2591%25E9%2580%259A%25208%252B128GB%2522%252C%2522shopid%2522%253A%252210706414%2522%252C%2522is_show_follow%2522%253A1%252C%2522isCart%2522%253A1%252C%2522is_show_purchaselist%2522%253A0%252C%2522is_subphoto%2522%253A%25220%2522%252C%2522extension_id%2522%253A%2522%257B%255C%2522ad%255C%2522%253A%255C%25221476%255C%2522%252C%255C%2522business_type%255C%2522%253A%255C%25222%255C%2522%252C%255C%2522campaign_type%255C%2522%253A%255C%25222%255C%2522%252C%255C%2522ch%255C%2522%253A%255C%25222%255C%2522%252C%255C%2522organic_atrribution_type%255C%2522%253A%255C%25220%255C%2522%252C%255C%2522sku%255C%2522%253A%255C%252210085976605508%255C%2522%252C%255C%2522ts%255C%2522%253A%255C%25221752481428%255C%2522%252C%255C%2522uniqid%255C%2522%253A%255C%2522%257B%255C%255C%255C%2522material_id%255C%255C%255C%2522%253A%255C%255C%255C%252252668325252%255C%255C%255C%2522%252C%255C%255C%255C%2522pos_id%255C%255C%255C%2522%253A%255C%255C%255C%25221476%255C%255C%255C%2522%252C%255C%255C%255C%2522sid%255C%255C%255C%2522%253A%255C%255C%255C%25228ec39a3c-fef1-4aa3-8c82-19986d8d9419%255C%255C%255C%2522%257D%255C%2522%257D%2522%252C%2522adStatus%2522%253A%25221%2522%252C%2522store_under_tag%2522%253A%2522-100%2522%252C%2522foreLabel%2522%253A%2522-100%2522%252C%2522InterestType%2522%253A%2522-100%2522%252C%2522comment_sale%2522%253A%25222000%252B%25E6%259D%25A1%25E8%25AF%2584%25E4%25BB%25B7%25401%25236000%252B%25E4%25BA%25BA%25E7%259C%258B%25E8%25BF%2587%25404%2522%252C%2522notResultType%2522%253A%2522-100%2522%252C%2522freqTag%2522%253A%2522-100%2522%252C%2522titleUnder%2522%253A%2522%2522%252C%2522titleLabelType%2522%253A%2522%2522%252C%2522firpricetype%2522%253A%252211%2522%252C%2522firprice%2522%253A%2522760%2522%252C%2522secpricetype%2522%253A%252244%2522%252C%2522secprice%2522%253A%2522820.00%2522%257D%24pinid%3DyXKCNYo40_HkD4yVQppJtw%24jdv%3D137579179%7Cdirect%7C-%7Cnone%7C-%7C1752201090365%24dataver%3D0.1&ref=https%3A%2F%2Fdev.jd.com%3A5173%2Fretail-mall%2Fmain_search%2Fpro%2F0.0.7%2F%3Fkeyword%3DSFDFDFD%2525%2525%26pvid%3Db68c42c2280940debf20017ed9c1dbc1&rm=1752483175485'

  // 将解析后的数据对象可视化到页面
  const rst = getQuery(urlStr)
  console.log(rst)
  document.getElementById('div1').appendChild(jsonParse(rst))
  // document.getElementById('div2').appendChild(jsonParse(getQuery(urlStr2)))
</script>
</body>
</html>